<?php

namespace common\models;

use Yii;
use yii\helpers\ArrayHelper;

/**
 * This is the model class for table "Employment".
 */
class Employment extends ActiveRecord {

    // 雇佣类型
    const TypeCustomized = 1;
    const TypeMonthly = 2;
    public static $typeSelector = [
        self::TypeCustomized => ['title' => '定制', 'status' => 'red'],
        self::TypeMonthly => ['title' => '全职', 'status' => 'blue'],
    ];

    // 状态
    // @describe 状态流转图
    // @describe 下单成功，待确认 -> 设计师已确认 -> 待付款 -> 付款中 -> 雇用成功 -> 已完成/中止
    const StatusInit = 0;
    const StatusConfirm = 1;
    const StatusPaying = 2;
    const StatusSuccess = 10;
    const StatusComplete = 11;
    const StatusAbort = 12;
    public static $statusSelector = [
        self::StatusInit => ['title' => '待确认', 'status' => 'gray'],
        self::StatusConfirm => ['title' => '已确认', 'status' => 'purple'],
        self::StatusPaying => ['title' => '付款中', 'status' => 'orange'],
        self::StatusSuccess => ['title' => '服务中', 'status' => 'blue'],
        self::StatusComplete => ['title' => '已完成', 'status' => 'green'],
        self::StatusAbort => ['title' => '中止', 'status' => 'red'],
    ];

    // only define rules for those attributes that
    // will receive user inputs.
    public function rules()
    {
        return [
            [['order_number', 'designer_id', 'employer_id', 'type', 'begin', 'hires'], 'required'],
            [['recruit_id', 'designer_id', 'employer_id', 'type', 'period', 'total_amount', 'coupon_id', 'deduct', 'payday', 'begin', 'finish', 'hires', 'status', 'deleted_at'], 'integer'],
            [['contact_mobile'], 'string', 'max' => 16],
            [['order_number'], 'string', 'max' => 32],
            [['describe', 'remark'], 'string', 'max' => 255],
        ];
    }
    /**
     * 字段名称
     * @return array
     */
    public function attributeLabels()
    {
        return [
            'order_number' => '订单号',
            'designer_id' => '设计师',
            'recruit_id' => '招聘编号',
            'employer_id' => '雇主',
            'contact_mobile' => '雇主联系方式',
            'type' => '雇佣类型',
            'period' => '期数',
            'hires' => '薪水',
            'total_amount' => '总金额',
            'coupon_id' => '优惠劵',
            'deduct' => '减免',
            'begin' => '开始日期',
            'finish' => '结束日期',
            'payday' => '发薪日',
            'status' => '状态',
            'describe' => '工作内容',
            'remark' => '备注',
            'deleted_at' => '删除时间',
        ];
    }

    /**
     * @name update & insert data check config for html
     * @param $type string 操作类型
     * @param $encodeJson boolean 是否转成JSON字符串
     * @return string | array
     */
    public static function flyer($type = 'update')
    {
        if($type == 'order') {
            return [
                'param' => [
                    'coupon_id' => ['优惠劵', ['int']],
                ],
            ];
        }
        else {
            return [
                'param' => [
                    'recruit_id' => ['招聘编号', ['int']],
                    'designer_id' => ['设计师', ['int', 'required']],
                    'type' => ['雇佣类型', ['inkey' => static::$typeSelector, 'required']],
                    'begin' => ['开始日期', ['date' => 'Y-m-d', 'required']],
                    'finish' => ['结束日期', ['date' => 'Y-m-d']],
                    'period' => ['期数', ['int', 'required']],
                    'hires' => ['薪水', ['float', 'required']],
                    'contact_mobile' => ['雇主联系方式', ['mobile', 'required']],
                    // 'payday' => ['发薪日', ['int', 'gt' => 0, 'let' => 28, 'required']],
                    'describe' => ['工作内容', ['maxlength' => 255, 'required']],
                ],
            ];
        }
    }

    /**
     * 计算配置信息
     * @return boolean
     */
    public function initParams()
    {
        // 定制雇佣时间规划
        if($this->type == static::TypeCustomized) {
            // $this->begin = date('Ymd');
            $period = $this->period - 1;
            $this->finish = date('Ymd', strtotime("+{$period} days", strtotime($this->begin)));
            $this->total_amount = $this->hires;
        }
        // 全职包月雇佣时间规划
        else if(key_exists($this->type, static::$typeSelector)) {
            // $this->begin = date('Ymd');
            $this->finish = date('Ymd', strtotime("+{$this->period} months, -1 days", strtotime($this->begin)));
            $this->total_amount = bcmul($this->hires, $this->period);
        }
        else {
            return false;
        }
        return true;
    }

    /**
     * 获取真是支付金额
     * @return integer
     */
    public function payAmount()
    {
        return bcsub($this->total_amount, $this->deduct);
    }

    /**
     * 设计师确认订单
     * @return boolean
     */
    public function confirm()
    {
        if($this->status != static::StatusInit) {
            return false;
        }
        $this->status = static::StatusConfirm;
        $this->updated_at = time();
        return $this->transaction(function($db) {
            if( ! $this->save()) {
                throw new \Exception('update employment status error');
            }
            // 记录日志
            if( ! $this->logger(['handler' => EmploymentLog::HandlerUser, 'remark' => '设计师已确认订单'])) {
                throw new \Exception('create employment log error');
            }
            return true;
        });
    }

    /**
     * 雇主开始付款
     * @return boolean
     */
    public function paying($coupon_id)
    {
        if($this->status != static::StatusConfirm) {
            return false;
        }
        $this->coupon_id = $coupon_id;
        $this->coupon_id || $this->coupon_id = 0;
        $deduct = 0;
        // 使用优惠劵
        if($this->coupon_id) {
            $order = [
                'amount' => $this->total_amount,
            ];
            // 优惠劵状态
            if(empty($this->userCoupon) || ( ! $this->userCoupon->usable($order))) {
                return false;
            }
            $deduct = $this->userCoupon->deduct($order);
            if($deduct >= $this->total_amount) {
                return false;
            }
        }
        $this->deduct = $deduct;
        $this->status = static::StatusPaying;
        $this->updated_at = time();
        return $this->transaction(function($db) {
            if( ! $this->save()) {
                throw new \Exception('update employment status error');
            }
            if($this->coupon_id) {
                if( ! $this->userCoupon->used()) {
                    throw new \Exception('update coupon status error');
                }
            }
            // 记录日志
            if( ! $this->logger(['handler' => EmploymentLog::HandlerUser, 'remark' => '雇主开始付款'])) {
                throw new \Exception('create employment log error');
            }
            return true;
        });
    }

    /**
     * 付款成功，服务中
     * @return boolean
     */
    public function success()
    {
        if($this->status != static::StatusPaying) {
            return false;
        }
        $this->status = static::StatusSuccess;
        $this->updated_at = time();
        if( ! $this->save()) {
            return false;
        }
        // 规划薪酬
        if( ! $this->createSalary()) {
            return false;
        }
        // 记录日志
        if( ! $this->logger(['handler' => EmploymentLog::HandlerOuter, 'remark' => '雇主付款完成，开始进入服务期'])) {
            return false;
        }
        return true;
    }
    /**
     * 规划薪酬结算日
     * @return boolean
     */
    public function createSalary()
    {
        $params = [];
        // 定制薪酬规划
        if($this->type == static::TypeCustomized) {
            $params[] = [
                'order_number' => EmploymentSalary::generateOrderNumber().'001',
                'employment_id' => $this->id,
                'employer_id' => $this->employer_id,
                'designer_id' => $this->designer_id,
                'paydate' => $this->finish,
                'begin' => $this->begin,
                'finish' => $this->finish,
                'hires' => $this->reckonHires(),
                'deduct' => 0,
                'status' => EmploymentSalary::StatusInit,
                'remark' => $this->begin.' ~ '.$this->finish.' 薪酬',
                'created_at' => time(),
                'updated_at' => time(),
            ];
        }
        // 全职包月薪酬规划
        else if(key_exists($this->type, static::$typeSelector)) {
            $baseTime = strtotime($this->begin);
            $orderNumber = EmploymentSalary::generateOrderNumber();
            for($i = 1; $i <= $this->period; ++$i) {
                $paydate = date('Ymd', strtotime("+1 months -1 days", $baseTime));
                $params[] = [
                    'order_number' => $orderNumber.str_repeat('0', 3 - strlen($i)).$i,
                    'employment_id' => $this->id,
                    'employer_id' => $this->employer_id,
                    'designer_id' => $this->designer_id,
                    'paydate' => $paydate,
                    'begin' => date('Ymd', $baseTime),
                    'finish' => $paydate,
                    'hires' => $this->reckonHires(),
                    'deduct' => 0,
                    'status' => EmploymentSalary::StatusInit,
                    'remark' => date('Ymd', $baseTime).' ~ '.$paydate.' 薪酬',
                    'created_at' => time(),
                    'updated_at' => time(),
                ];
                $baseTime = strtotime('+1 days', strtotime($paydate));
            }
        }
        else {
            return false;
        }
        return EmploymentSalary::batchInsert($params);
    }
    /**
     * 计算设计师应得金额
     */
    public function reckonHires()
    {
        return $this->hires;
    }

    /**
     * 设置薪酬记录结算成功
     * @return boolean
     */
    public function complete()
    {
        if($this->status != static::StatusSuccess) {
            return false;
        }
        $this->status = static::StatusComplete;
        $this->updated_at = time();
        return $this->transaction(function($db) {
            if( ! $this->save()) {
                return false;
            }
            // 记录日志
            return $this->logger(['handler' => EmploymentLog::HandlerSystem, 'remark' => '服务到期，完成雇佣关系']);
        });
    }
    /**
     * 中止雇佣关系
     * @return boolean
     */
    public function abort()
    {
        if($this->status != static::StatusSuccess) {
            return false;
        }
        $this->status = static::StatusAbort;
        $this->updated_at = time();
        if( ! $this->save()) {
            return false;
        }
        // 规划薪酬
        $updateAttributes = ['status' => EmploymentSalary::StatusClose, 'updated_at' => time()];
        $updateConditions = ['employment_id' => $this->id, 'status' => [EmploymentSalary::StatusInit, EmploymentSalary::StatusPaying]];
        if( ! EmploymentSalary::updateAll($updateAttributes, $updateConditions)) {
            return false;
        }
        // 记录日志
        if( ! $this->logger(['handler' => EmploymentLog::HandlerAdmin, 'remark' => '雇佣关系中止'])) {
            return false;
        }
        return true;
    }
    /**
     * 暂停雇佣订单
     * @param $params array 暂停参数
     * @return boolean
     */
    public function applyPause($params)
    {
        if(empty($params['days'])) {
            return false;
        }
        if($this->canPause()) {
            return false;
        }
        $params['start_day'] = date('Ymd', strtotime($params['start_day']));
        $params['employment_id'] = $this->id;
        return $this->transaction(function($db) use ($params) {
            if( ! EmploymentPause::creator($params)) {
                throw new \Exception('create employment pause log error');
            }
            // 记录日志
            if( ! $this->logger(['handler' => EmploymentLog::HandlerUser, 'remark' => '雇主申请暂停雇佣关系'])) {
                throw new \Exception('create employment log error');
            }
            return true;
        });
    }

    /**
     * 确认暂停雇佣关系
     * @return boolean
     */
    public function confirmPause()
    {
        if( ! in_array($this->status, [static::StatusSuccess])) {
            return false;
        }
        /** @var EmploymentPause $pause */
        $pause = EmploymentPause::find()->where(['employment_id' => $this->id, 'status' => [EmploymentPause::StatusInit, EmploymentPause::StatusConfirm]])->one();
        if(empty($pause) || ($pause->start_day < date('Ymd'))) {
            return false;
        }
        $this->finish = date('Ymd', strtotime("+{$pause['days']} days", strtotime($this->finish)));
        $this->updated_at = time();
        return $this->transaction(function($db) use ($pause) {
            if( ! $this->save()) {
                throw new \Exception('update employment error');
            }
            if( ! $this->pauseSalary($pause->start_day, $pause->days)) {
                throw new \Exception('update pause employment salary error');
            }
            if( ! $pause->success()) {
                throw new \Exception('update employment pause error');
            }
            // 记录日志
            if( ! $this->logger(['handler' => EmploymentLog::HandlerUser, 'remark' => '设计师确认暂停雇佣关系'])) {
                throw new \Exception('create employment log error');
            }
            return true;
        });
    }

    /**
     * 推迟雇佣发薪日时间
     * @return boolean
     */
    public function pauseSalary($start, $days)
    {
        $salarys = $this->employmentSalarys;
        foreach($salarys as $salary) {
            if( ! in_array($salary->status, [EmploymentSalary::StatusInit]) || ($salary->paydate < $start)) {
                continue;
            }
            $salary->updated_at = time();
            $salary->finish = date('Ymd', strtotime("+{$days} days", strtotime($salary->finish)));
            $salary->paydate = $salary->finish;
            if($salary->begin > $start) {
                $salary->begin = date('Ymd', strtotime("+{$days} days", strtotime($salary->begin)));
            }
            if( ! $salary->save()) {
                return false;
            }
        }
        return true;
    }
    /**
     * 雇主确认发薪
     * @return boolean
     */
    public function confirmPayment()
    {
        $salaries = EmploymentSalary::find()
            ->where(['employment_id' => $this->id, 'status' => EmploymentSalary::StatusInit])
            ->andWhere(['<=', 'paydate', date('Ymd')])
            ->all();
        if(empty($salaries)) {
            return true;
        }
        foreach($salaries as $salary) {
            $salary->confirm();
        }
        return true;
    }

    /**
     * 申诉雇佣订单
     * @param $params array 申诉参数
     * @return boolean
     */
    public function applyAppeal($params)
    {
        if($this->canAppeal()) {
            return false;
        }
        $params['employment_id'] = $this->id;
        $params['designer_id'] = $this->designer_id;
        $params['employer_id'] = $this->employer_id;
        return $this->transaction(function($db) use ($params) {
            if( ! EmploymentAppeal::creator($params)) {
                throw new \Exception('create employment appeal log error');
            }
            // 记录日志
            if( ! $this->logger(['handler' => EmploymentLog::HandlerUser, 'remark' => ($params['handler'] == EmploymentAppeal::HandlerDesigner ? '设计师' : '雇主').'申诉'])) {
                throw new \Exception('create employment log error');
            }
            return true;
        });
    }
    /**
     * 添加日志
     * @param $param array 数据数组
     * @return boolean
     */
    public function logger($param)
    {
        $param['employment_id'] = $this->id;
        return EmploymentLog::logger($param);
    }

    /**
     * 获取designer信息
     * @return \yii\db\ActiveQuery
     */
    public function getDesigner()
    {
        return $this->hasOne(User::className(), ['id' => 'designer_id']);
    }

    /**
     * 获取employer信息
     * @return \yii\db\ActiveQuery
     */
    public function getEmployer()
    {
        return $this->hasOne(User::className(), ['id' => 'employer_id']);
    }

    /**
     * 获取UserCoupon信息
     * @return \yii\db\ActiveQuery
     */
    public function getUserCoupon()
    {
        return $this->hasOne(UserCoupon::className(), ['id' => 'coupon_id']);
    }

    /**
     * 获取薪水发放记录
     * @return \yii\db\ActiveQuery
     */
    public function getEmploymentSalarys()
    {
        return $this->hasMany(EmploymentSalary::className(), ['employment_id' => 'id']);
    }

    /**
     * 获取充值成功记录
     * @return \yii\db\ActiveQuery
     */
    public function getEmploymentRecharge()
    {
        return $this->hasOne(EmploymentRecharge::className(), ['employment_id' => 'id', 'status' => [EmploymentRecharge::StatusSuccess]]);
    }

    /**
     * 获取充值申请记录
     * @return \yii\db\ActiveQuery
     */
    public function getEmploymentRecharges()
    {
        return $this->hasMany(EmploymentRecharge::className(), ['employment_id' => 'id']);
    }

    /**
     * 获取雇佣订单的暂停记录
     * @return \yii\db\ActiveQuery
     */
    public function getEmploymentPauses()
    {
        return $this->hasMany(EmploymentPause::className(), ['employment_id' => 'id']);
    }

    /**
     * 获取雇佣订单正在进行中的暂停记录
     * @return \yii\db\ActiveQuery
     */
    public function getEmploymentPause()
    {
        return $this->hasOne(EmploymentPause::className(), ['employment_id' => 'id', 'status' => [EmploymentPause::StatusInit, EmploymentPause::StatusConfirm]]);
    }

    /**
     * 判断是否可暂停
     * @return boolean
     */
    public function canPause()
    {
        if($this->deleted()) {
            return false;
        }
        if( ! in_array($this->status, [static::StatusSuccess])) {
            return false;
        }
        return EmploymentPause::find()->where(['employment_id' => $this->id, 'status' => [EmploymentPause::StatusInit, EmploymentPause::StatusConfirm]])->exists();
    }

    /**
     * 判断是否可申诉
     * @return boolean
     */
    public function canAppeal()
    {
        if($this->deleted()) {
            return false;
        }
        if( ! in_array($this->status, [static::StatusSuccess])) {
            return false;
        }
        return EmploymentAppeal::find()->where(['employment_id' => $this->id, 'status' => [EmploymentAppeal::StatusInit, EmploymentAppeal::StatusAccept]])->exists();
    }

    /**
     * 获取操作记录
     * @return \yii\db\ActiveQuery
     */
    public function getEmploymentLogs()
    {
        return $this->hasMany(EmploymentLog::className(), ['employment_id' => 'id'])->orderBy('id desc');
    }

    /**
     * 获取可用优惠劵
     * @return array
     */
    public function getCouponSelector()
    {
        return ArrayHelper::map(UserCoupon::findor($this->employer_id, Coupon::TypeEmployment), 'id', 'title');
    }

    /**
     * 生成订单号
     * @return string
     */
    public static function generateOrderNumber()
    {
        return strtoupper('E'.base_convert((microtime(true) * 10000).rand(10000, 99999).rand(10000, 99999), 10, 36));
    }

    /**
     * 检查是否已经与设计师建立了雇佣关系
     * @return boolean
     */
    public static function isEmployed($designerId)
    {
        return Employment::find()->where(['designer_id' => $designerId, 'employer_id' => Yii::$app->user->id])
            ->andWhere(['status' => [static::StatusInit, static::StatusConfirm, static::StatusPaying, static::StatusSuccess]])
            ->exists();
    }

    /**
     * 建立雇佣关系
     * @return mixed
     */
    public static function employ($params)
    {
        $params['status'] = static::StatusInit;
        $employment = new static();
        $employment->loads($params);
        return $employment->save();
    }
}